#!/user/bin/env python
# -*- coding: utf-8 -*-
''' 
Author: Sjy
LastEditTime: 2024-08-27 11:43:36
Description:  文件处理通用工具
'''
__all__= []
import types
import traceback
import re
import datetime 
import hashlib

def is_fun(value):
    """判断是否为函数"""
    return isinstance(value, type(lambda x: x))    
def is_dict(value):
    """判断是否为字典"""
    return isinstance(value, dict)
def is_list(value):
    """判断是否为列表"""
    return isinstance(value, list)
def print_error():
    traceback.print_exc()
def is_empty(value):
    """判断是否为空"""
    if value is None:
        return True
    if isinstance(value, str) and value.strip() == '':
        return True
    if isinstance(value, list) and len(value) == 0:
        return True
    if isinstance(value, dict) and len(value) == 0:
        return True
    return False
def quote_path_with_spaces(path:str=None):
    """将命令路径字符串转成可被start执行的路径
        1. 需要在路径间加上双引号已处理带有空格的路径
    """
    if not path: return path
    parts = path.replace("/", "\\").split("\\")
    quoted_parts = [f'"{part}"' if " " in part else part for part in parts]
    quoted_path = "\\".join(quoted_parts)
    quoted_path = quoted_path.replace("\\\\", "\\")  # 将双斜杠替换为单斜杠
    return quoted_path
def get_date_later(originalDate:str=None, days:int=0):
    """获取指定日期时间后的日期
    Args:
        originalDate (str, optional): 初始日期，格式为"2024-10-31". 如果值为None 则取当天.
        days (int, optional): 指定的时间，默认值为0.
    Returns:
        str: 返回指定日期时间后的日期，格式为"2024-10-31"
    """
    converted_date = datetime.date.today()
    if originalDate:
        converted_date = datetime.date(*map(int, originalDate.split("-")))
    laterDate =  converted_date + datetime.timedelta(days=days)
    return laterDate.strftime("%Y-%m-%d")
def get_years_today(originalDate:str=None, year:int=1):
    """获取指定多少年后的今天日期
    Args:
        无
    Returns:
        str: 返回指定日期时间后的日期，格式为"2024-10-31"
    """
    todayStr = originalDate or  datetime.date.today().strftime("%Y-%m-%d")
    """根据当日按日期字符串 2024-10-31 分割成年月日"""
    yearToday = todayStr.split("-")
    """将年份加上指定的年份"""
    yearToday[0] = str(int(yearToday[0]) + year)
    """将年月日拼接成字符串"""
    laterDate = "-".join(yearToday)
    return laterDate
def compare_date(date1:str, date2:str):
    """比较两个日期的大小
    Args:
        date1 (str): 日期1
        date2 (str): 日期2
    Returns:
        int: 1: date1 > date2, 0: date1 = date2, -1: date1 < date2
    """
    date1 = datetime.datetime.strptime(date1, "%Y-%m-%d")
    date2 = datetime.datetime.strptime(date2, "%Y-%m-%d")
    if date1 > date2:
        return 1
    elif date1 == date2:
        return 0
    else:
        return -1
def hash_str(src:str):
    """获取字符串的hash值
    Args:
        src (str): 字符串
    Returns:
        str: 字符串的hash值
    """
    hash_object = hashlib.md5(src.encode())
    hex_dig = hash_object.hexdigest()
    return hex_dig
def hash_file(src:str, hashType:str="md5"):
    """获取文件的hash值
    Args:
        src (str): 文件路径
        hashType (str, optional): hash类型，默认为md5，可选值为 sha256, md5. 
    Returns:
        str: 文件的hash值
    """
    # 初始化 MD5 和 SHA-256 Hash 对象
    ins = hashlib.md5()
    if hashType == "sha256":
        ins = hashlib.sha256()
    with open(src, 'rb') as f:
        # 逐块读取文件，避免大文件一次性加载
        for byte_block in iter(lambda: f.read(4096), b""):
            ins.update(byte_block)
    return  ins.hexdigest()

def get_all_subclasses(module, parent_class):
    """获取模块中含有父类的子类
    Args:
        module (_type_): _description_
        parent_class (_type_): _description_
    Returns:
        _type_: _description_
    """
    subclasses = []
    for name, obj in module.__dict__.items():
        if isinstance(obj, type) and issubclass(obj, parent_class) and obj != parent_class:
            subclasses.append(obj)
    return subclasses


def update_data(oldData:dict={}, data:dict={}, delKeys=[]) -> dict:
    """更新数据
    Args:
        oldData (dict, optional): 旧数据. Defaults to {}.
        data (dict, optional): 新数据. Defaults to {}.
        delKeys (list, optional): 需要删除的key. Defaults to [].
    Returns:
        dict: 返回更新后的数据
    """
    saveData = {**oldData, **data} 
    for key in delKeys:
        if key in saveData:
            saveData.pop(key)
    return saveData

def recursion_update_data(oldData:dict={}, data:dict={}, delKeys=[]) -> dict:
    """递归更新数据
    Args:
        oldData (dict, optional): 旧数据. Defaults to {}.
        data (dict, optional): 新数据. Defaults to {}.
        delKeys (list, optional): 需要删除的key. Defaults to [].
    Returns:
        dict: 返回更新后的数据
    """
    def recursion_update(oldData, data, delKeys=[]):
        if isinstance(data, dict): 
            for key in delKeys:
                if key in oldData:
                    oldData.pop(key)
                    
            saveData = {**oldData}
            for key, value in data.items():
                if isinstance(value, dict):
                    saveData[key] = recursion_update(saveData.get(key, {}), value)
                elif isinstance(value, list):
                    saveData[key] = recursion_update(saveData.get(key, []), value)
                elif value != None:
                    saveData[key] = value
            return saveData
        elif isinstance(data, list):
            saveData = []
            for index, value in enumerate(data):
                if isinstance(value, dict):
                    recOld = (len(oldData)-1) >= index and oldData[index] or {}
                    saveData.append(recursion_update(recOld, value))
                elif isinstance(value, list):
                    recOld = len(oldData)-1 >= index  and oldData[index] or [] 
                    saveData.append(recursion_update(recOld, value))
                else:
                    saveData.append(value)
            return saveData
    return recursion_update(oldData, data, delKeys=delKeys)

def test_fun(funName:str, dist:any, *args, **kwargs):
    """测试函数
        funName: 函数名
        后面的参数为函数的参数
    """
    print(f"\033[34m ============= test {funName} begin =============\033[0m")
    try:
        fun = globals().get(funName)
        if fun:
            result = fun(*args, **kwargs)
            if result != dist:
                test_error(dist, result)
            else:
                print(f"\033[32m {funName} function is ok!!!\033[0m")
        else:
            print(f"\033[31m function {funName} not found!!!\033[0m")
    except:
        print_error()
    print()
def test_error(dst, result):
    print("\033[31m test error!!!\033[0m")
    print(f"\t \033[33m expect: {dst}\033[0m")
    print(f"\t \033[31m result: {result}\033[0m")




if __name__ == "__main__":
    
    def test_quote_path_with_spaces():
        src = "C:\\Program Files\\file.txt"
        dist = "C:\\\"Program Files\"\\file.txt"
        test_fun("quote_path_with_spaces", dist, src)
    def test_get_date_later():
        src =  "2024-10-31"
        dist = "2024-11-02"
        test_fun("get_date_later", dist, src, 2)
    def test_get_years_today():
        src =  "2024-10-30"
        dist = "2025-10-30"
        test_fun("get_years_today", dist, src)
    def test_hash_str():
        src = "Hello, World"
        dist = "82bb413746aee42f89dea2b59614f9ef"
        test_fun("hash_str", dist, src)
    def test_get_all_subclasses():
        # 示例模块
        # 使用函数
        import tests.test_subclass as test_subclass  # 替换为实际模块名
        dist = [test_subclass.Child1, test_subclass.Child2]
        test_fun("get_all_subclasses", dist, test_subclass, test_subclass.Parent)
    test_quote_path_with_spaces()
    test_get_date_later()   
    test_get_years_today()
    test_hash_str()
    test_get_all_subclasses()




